//
//  PositioningHelper.m
//  etionMisc
//
//  Created by wangjian on 4/26/13.
//
//

#import "XLGPositioningHelper.h"

#import <BaiduMapAPI_Base/BMKBaseComponent.h>
#import <BaiduMapAPI_Location/BMKLocationComponent.h>
#import <BaiduMapAPI_Search/BMKSearchComponent.h>
#import <BaiduMapAPI_Utils/BMKUtilsComponent.h>

@interface BMKPlanNode (XLGPositioningHelper)<XLGPositingPlanNode>

@end

@interface BMKReverseGeoCodeResult (XLGPositioningHelper)<XLGPositingReverseGeoCodeResult>

@end

#pragma mark - XLGPositioningObj

typedef void(^GetResultAsyncCompletion)(BOOL finished, id result, NSError *error);
typedef void(^GetCoordAsyncCompletion)(BOOL finished, CLLocation *location, NSError *error);
typedef void(^GetCoordByAddrAsyncCompletion)(BOOL finished, CLLocation *location, NSString *address, NSError *error);

@interface XLGPositioningObj : NSObject
{
    CLLocationCoordinate2D _coord;
    id<XLGPositioningHelperDelegate> _receiver;
    id _userInfo;
    GetResultAsyncCompletion _getResultAsyncComplete;
    GetCoordAsyncCompletion _getCoordAsyncComplete;
    BMKPlanNode *_startNode;
    BMKPlanNode *_endNode;
    XLGRouteSearchType _routeType;
}
@property (nonatomic, assign) CLLocationCoordinate2D coord;
@property (nonatomic, assign) id<XLGPositioningHelperDelegate> receiver;
@property (nonatomic, retain) NSString *city;
@property (nonatomic, retain) NSString *address;
@property (nonatomic, retain) id userInfo;
@property (nonatomic, copy) GetResultAsyncCompletion getResultAsyncComplete;
@property (nonatomic, copy) GetCoordAsyncCompletion getCoordAsyncComplete;
@property (nonatomic, copy) GetCoordByAddrAsyncCompletion getCoordByAddrAsyncComplete;
@property (nonatomic, retain) BMKPlanNode *startNode;
@property (nonatomic, retain) BMKPlanNode *endNode;
@property (nonatomic, assign) XLGRouteSearchType routeType;

@end

@implementation XLGPositioningObj

- (void)dealloc
{
    [_userInfo release_mm];
    [_getResultAsyncComplete release_mm];
    [_getCoordAsyncComplete release_mm];
    [_getCoordByAddrAsyncComplete release_mm];
    [_startNode release_mm];
    [_endNode release_mm];
    [_address release_mm];
    [_city release_mm];
    superdealloc_mm;
}

@end

#pragma mark - XLGPositioningHelper

static XLGPositioningHelper *sPositioningHelper;

@interface XLGPositioningHelper ()<BMKLocationServiceDelegate, BMKGeoCodeSearchDelegate, BMKRouteSearchDelegate, CLLocationManagerDelegate>
{
    NSMutableArray *_arCoordPositioningObjs;
    NSMutableArray *_arCoordByAddrPositioningObjs;
    NSMutableArray *_arAddrPositioningObjs;
    NSMutableArray *_arRoutePositioningObjs;
    
//    NSLock *_coordlock;
//    NSLock *_coordByAddrLock;
//    NSLock *_addrLock;
//    NSLock *_routeLock;
    
    XMSDispatchQueueWrapper *_lockQueue;
    
    BOOL _bGettingCoord;
    BOOL _bGettingCoordByAddr;
    BOOL _bGettingAddr;
    BOOL _bGettingRoute;
}

@property (nonatomic, retain) BMKLocationService *locationService;
@property (nonatomic, retain) BMKGeoCodeSearch *geoCodeSearch;
@property (nonatomic, retain) BMKGeoCodeSearch *reverseGeoCodeSearch;
@property (nonatomic, retain) BMKRouteSearch *routeSearch;
@property (nonatomic, retain) CLLocationManager *locationManager;

@end

@implementation XLGPositioningHelper

+ (void)openService
{
    if (sPositioningHelper == nil)
    {
        sPositioningHelper = [[XLGPositioningHelper alloc] init];
    }
}

+ (void)closeService
{
    [sPositioningHelper release];
    sPositioningHelper = nil;
}

+ (BOOL)isNetworkReachable
{
/*
        // Create zero addy
    struct sockaddr_in zeroAddress;
    bzero(&zeroAddress, sizeof(zeroAddress));
    zeroAddress.sin_len = sizeof(zeroAddress);
    zeroAddress.sin_family = AF_INET;
    
    // Recover reachability flags
    SCNetworkReachabilityRef defaultRouteReachability = SCNetworkReachabilityCreateWithAddress(NULL, (struct sockaddr *)&zeroAddress);
    SCNetworkReachabilityFlags flags;
    
    BOOL didRetrieveFlags = SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags);
    CFRelease(defaultRouteReachability);
    
    if (!didRetrieveFlags)
    {
        return NO;
    }
    
    BOOL isReachable = flags & kSCNetworkFlagsReachable;
    BOOL needsConnection = flags & kSCNetworkFlagsConnectionRequired;
    return (isReachable && !needsConnection) ? YES : NO;
    */
    
    return ![[XMSNetWorkReachability reachability] isNotReachable];
}

+ (BOOL)locationServiceVisible
{
    if ([XLGPositioningHelper systemServiceVisible] == NO || [XLGPositioningHelper appServiceVisible] == NO)
    {
        return NO;
    }
    return YES;
}

+ (BOOL)systemServiceVisible
{
    return [CLLocationManager locationServicesEnabled];
}

+ (BOOL)appServiceVisible
{
    BOOL isArrowLocation = NO;
    if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusAuthorizedWhenInUse ||
        [CLLocationManager authorizationStatus] == kCLAuthorizationStatusAuthorizedAlways)
    {
        isArrowLocation = YES;
    }
    return isArrowLocation;
}

//+ (BOOL)getCoordWithReceiver:(id<XLGPositioningHelperDelegate>)receiver userInfo:(id)userInfo
//{
//    if ([self serviceVisible] == NO)
//    {
//        return NO;
//    }
//    [XLGPositioningHelper openService];
//    return [sPositioningHelper addCoordReceiver:receiver userInfo:userInfo completeBlock:nil];
//}

//+ (BOOL)getAddrWithReceiver:(id<XLGPositioningHelperDelegate>)receiver coordinate:(CLLocationCoordinate2D)coord userInfo:(id)userInfo
//{
//    if ([self serviceVisible] == NO || coord.latitude==0 || coord.longitude == 0)
//    {
//        return NO;
//    }
//    [XLGPositioningHelper openService];
//    return [sPositioningHelper addAddrReceiver:receiver coordinate:coord userInfo:userInfo completeBlock:nil];
//}

+ (void)getCoordWithReceiver:(id<XLGPositioningHelperDelegate>)receiver completion:(void (^)(BOOL finished, CLLocation *location, NSError *error))completion
{
    [XLGPositioningHelper openService];
    [sPositioningHelper addCoordReceiver:receiver userInfo:nil completeBlock:completion];
}

+ (void)getCoordWithReceiver:(id<XLGPositioningHelperDelegate>)receiver addr:(NSString *)addr city:(NSString *)city completion:(void (^)(BOOL finished, CLLocation *location, NSString *address, NSError *error))completion
{
//    if ([XLGPositioningHelper systemServiceVisible] == NO)
//    {
//        DispatchMainThread(NO, ^
//        {
//            CLLocationCoordinate2D location = CLLocationCoordinate2DMake(0.0, 0.0);
//            NSError *error = [NSError errorWithDomain:@"定位服务不可用" code:XLGPositioningErrorSystemServiceDisable userInfo:nil];
//            if(completion == nil)
//                [receiver positioningHelperUpdateCoord:location userInfo:nil error:error];
//            else if(completion != nil)
//                completion(NO, location, nil, error);
//        });
//    }
//    else
    if (addr.length == 0)
    {
        DispatchMainThread(NO, ^
        {
            CLLocation *location = [[[CLLocation alloc] initWithLatitude:0.0 longitude:0.0] autorelease_mm];
            NSError *error = [NSError errorWithDomain:@"非法坐标" code:XLGPositioningErrorFail userInfo:nil];
            if(completion == nil)
                [receiver positioningHelperUpdateCoord:location userInfo:nil error:error];
            else if(completion != nil)
                completion(NO, location, nil, error);
        });
    }
    else
    {
        [XLGPositioningHelper openService];
        [sPositioningHelper addCoordReceiver:receiver addr:addr city:city userInfo:nil completeBlock:completion];
    }
}

+ (void)getAddrWithReceiver:(id<XLGPositioningHelperDelegate>)receiver coordinate:(CLLocationCoordinate2D)coord completion:(void (^)(BOOL finished, id<XLGPositingReverseGeoCodeResult> addrInfo, NSError *error))completion
{
    if([XLGPositioningHelper isNetworkReachable] == NO)
    {
        DispatchMainThread(NO, ^
        {
           NSError *error = [NSError errorWithDomain:@"定位服务不可用" code:XLGPositioningErrorNetworkNotReachable userInfo:nil];
           if(completion == nil)
               [receiver positioningHelperUpdateAddr:nil userInfo:nil error:error];
           else if(completion != nil)
               completion(NO, nil, error);
        });
    }
    else if (coord.latitude ==0 || coord.longitude == 0)
    {
        DispatchMainThread(NO, ^
        {
            NSError *error = [NSError errorWithDomain:@"非法坐标" code:XLGPositioningErrorFail userInfo:nil];
            if(completion == nil)
                [receiver positioningHelperUpdateAddr:nil userInfo:nil error:error];
            else if(completion != nil)
                completion(NO, nil, error);
        });
    }
    else
    {
        [XLGPositioningHelper openService];
        [sPositioningHelper addAddrReceiver:receiver coordinate:coord userInfo:nil completeBlock:completion];
    }
}

+ (void)getRouteWithReceiver:(id<XLGPositioningHelperDelegate>)receiver startNode:(BMKPlanNode *)startNode endNode:(BMKPlanNode *)endNode type:(XLGRouteSearchType)type completion:(void (^)(BOOL finished, id result, NSError *error))completion
{
    if ([XLGPositioningHelper isNetworkReachable] == NO)
    {
        DispatchMainThread(NO, ^
        {
            if(completion != nil)
                completion(NO, nil, [NSError errorWithDomain:@"定位服务不可用" code:XLGPositioningErrorNetworkNotReachable userInfo:nil]);
        });
    }
    else
    {
        [XLGPositioningHelper openService];
        [sPositioningHelper addRouteReceiver:receiver startNode:startNode endNode:endNode type:type completeBlock:completion];
    }
}

+ (BOOL)cancelGetCoordWithReceiver:(id<XLGPositioningHelperDelegate>)receiver
{
    return [sPositioningHelper cancelGetCoordWithReceiver:receiver];
}

+ (BOOL)cancelGetCoordByAddrWithReceiver:(id<XLGPositioningHelperDelegate>)receiver
{
    return [sPositioningHelper cancelGetCoordByAddrWithReceiver:receiver];
}

+ (BOOL)cancelGetAddrWithReceiver:(id<XLGPositioningHelperDelegate>)receiver
{
    return [sPositioningHelper cancelGetAddrWithReceiver:receiver];
}

+ (BOOL)cancelGetRouteWithReceiver:(id<XLGPositioningHelperDelegate>)receiver
{
    return [sPositioningHelper cancelGetRouteWithReceiver:receiver];
}

+ (void)cancelReceiver:(id<XLGPositioningHelperDelegate>)recviver
{
    [sPositioningHelper cancelReceiver:recviver];
}

- (id)init
{
    self = [super init];
    if (self)
    {
        _arCoordPositioningObjs = [[NSMutableArray alloc] init];
        _arCoordByAddrPositioningObjs = [[NSMutableArray alloc] init];
        _arAddrPositioningObjs = [[NSMutableArray alloc] init];
        _arRoutePositioningObjs = [[NSMutableArray alloc] init];
        
        _lockQueue = [[XMSDispatchQueueWrapper alloc]initWithName:[NSStringFromClass([self class]) UTF8String]];
    }
    return self;
}

- (void)dealloc
{
    [self cancelGetCoord];
    [self cancelGetCoordByAddr];
    [self cancelGetAddress];
    [self cancelGetRoute];
    
    [_arCoordPositioningObjs release_mm];
    [_arCoordByAddrPositioningObjs release_mm];
    [_arAddrPositioningObjs release_mm];
    [_arRoutePositioningObjs release_mm];
    [_lockQueue release_mm];
    [_locationManager release_mm];
    superdealloc_mm;
}

- (void)cancelReceiver:(id<XLGPositioningHelperDelegate>)recviver
{
    [_lockQueue dispatchThreadSync:^
    {
        [self cancelGetCoordWithReceiver:recviver];
        [self cancelGetCoordByAddrWithReceiver:recviver];
        [self cancelGetAddrWithReceiver:recviver];
        [self cancelGetRouteWithReceiver:recviver];
    }];
}

#pragma mark - get coord

- (BOOL)addCoordReceiver:(id<XLGPositioningHelperDelegate>)receiver userInfo:(id)userInfo completeBlock:(GetCoordAsyncCompletion)completeBlock
{
    [_lockQueue dispatchThreadSync:^
    {
        XLGPositioningObj *obj = [[XLGPositioningObj new] autorelease_mm];
        obj.receiver = receiver;
        obj.userInfo = userInfo;
        obj.getCoordAsyncComplete = completeBlock;
        [_arCoordPositioningObjs addObject:obj];
        if (_bGettingCoord == NO)
        {
            [self beginGetCoord];
        }
    }];
    return YES;
}

- (BOOL)cancelGetCoordWithReceiver:(id<XLGPositioningHelperDelegate>)receiver
{
    [_lockQueue dispatchThreadSync:^
    {
        NSMutableArray *arDel = [[NSMutableArray new] autorelease_mm];
        for (XLGPositioningObj *obj in _arCoordPositioningObjs)
        {
            if (obj.receiver == receiver)
            {
                [arDel addObject:obj];
            }
        }
        [_arCoordPositioningObjs removeObjectsInArray:arDel];
        
        if (_arCoordPositioningObjs.count == 0)
        {
            [self releaseGetCoord];
        }
    }];
    return YES;
}

- (void)beginGetCoord
{
    if (self.locationService == nil)
    {
        self.locationService = [[[BMKLocationService alloc] init] autorelease_mm];
    }
    
    self.locationService.delegate = self;
    [self.locationService startUserLocationService];
//    if (self.locationManager == nil)
//    {
//        self.locationManager = [[[CLLocationManager alloc] init] autorelease_mm];
//        self.locationManager.delegate = self;
//    }
//    [self.locationManager startUpdatingLocation];
    _bGettingCoord = YES;
}

- (void)cancelGetCoord
{
    self.locationService.delegate = nil;
    self.locationManager.delegate = self;
    
    //如果弹出了系统定位开启提示框，不能关闭定位服务，否则提示框会强制关闭，但我们又不能通过api获知是否弹出过提示框
    if([XLGPositioningHelper systemServiceVisible] == NO)
        ;
    else
    {
        [self.locationService stopUserLocationService];
        [self.locationManager stopUpdatingLocation];
        self.locationManager = nil;
        self.locationService = nil;
    }
    
    _bGettingCoord = NO;
}

- (void)releaseGetCoord
{
    [self cancelGetCoord];
    [self.locationService stopUserLocationService];
    self.locationService = nil;
}

- (void)didUpdateBMKUserLocation:(BMKUserLocation *)userLocation
{
    [self.locationService stopUserLocationService];
    [self publicLocation:userLocation.location error:nil];
}

- (void)didFailToLocateUserWithError:(NSError *)error
{
    NSError *err = nil;
    if([XLGPositioningHelper systemServiceVisible] == NO)
    {
        err = [NSError errorWithDomain:@"系统定位服务不可用" code:XLGPositioningErrorSystemServiceDisable userInfo:nil];
        [self publicLocation:[[[CLLocation alloc] initWithLatitude:0.0 longitude:0.0] autorelease_mm] error:err];
    }
    else if([XLGPositioningHelper appServiceVisible] == NO)
    {
        err = [NSError errorWithDomain:@"应用定位服务不可用" code:XLGPositioningErrorAppServiceDisable userInfo:nil];
        [self publicLocation:[[[CLLocation alloc] initWithLatitude:0.0 longitude:0.0] autorelease_mm] error:err];
    }
    else
    {
        if (self.locationManager == nil)
        {
            self.locationManager = [[[CLLocationManager alloc] init] autorelease_mm];
            self.locationManager.delegate = self;
        }
        [self.locationManager startUpdatingLocation];
    }
}
    
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
    [self.locationManager stopUpdatingLocation];
    
    if (locations.count > 0)
    {
        CLLocation *location = [locations objectAtIndex:0];
        CLLocationCoordinate2D baiduLocation = BMKCoorDictionaryDecode(BMKConvertBaiduCoorFrom(location.coordinate, BMK_COORDTYPE_GPS));
        [self publicLocation:[[[CLLocation alloc] initWithLatitude:baiduLocation.latitude longitude:baiduLocation.longitude] autorelease_mm] error:nil];
    }
    else
    {
        [self publicLocation:[[[CLLocation alloc] initWithLatitude:0.0 longitude:0.0] autorelease_mm] error:[NSError errorWithDomain:@"系统GPS定位失败" code:XLGPositioningErrorFail userInfo:nil]];
    }
}

- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
    [self.locationManager stopUpdatingLocation];
    [self publicLocation:[[[CLLocation alloc] initWithLatitude:0.0 longitude:0.0] autorelease_mm] error:[NSError errorWithDomain:@"系统GPS定位失败" code:XLGPositioningErrorFail userInfo:nil]];
}

- (void)publicLocation:(CLLocation *)location error:(NSError *)error
{
    [_lockQueue dispatchThreadSync:^
    {
        for (XLGPositioningObj *obj in _arCoordPositioningObjs)
        {
            GetCoordAsyncCompletion getCoordAsyncComplete = obj.getCoordAsyncComplete;
            id<XLGPositioningHelperDelegate> receiver = obj.receiver;
            
            DispatchMainThreadAsync(^
            {
                if (getCoordAsyncComplete == nil)
                {
                    [receiver positioningHelperUpdateCoord:location userInfo:obj.userInfo error:error];
                }
                else
                {
                    getCoordAsyncComplete(error == nil, location, error);
                }
            });
        }
        [_arCoordPositioningObjs removeAllObjects];

        [self cancelGetCoord];
    }];
}

#pragma mark - get coord by address

- (BOOL)addCoordReceiver:(id<XLGPositioningHelperDelegate>)receiver addr:(NSString *)addr city:(NSString *)city userInfo:(id)userInfo completeBlock:(GetCoordByAddrAsyncCompletion)completeBlock
{
    [_lockQueue dispatchThreadSync:^
    {
        XLGPositioningObj *obj = [[XLGPositioningObj new] autorelease_mm];
        obj.receiver = receiver;
        obj.address = addr;
        obj.city = city;
        obj.userInfo = userInfo;
        obj.getCoordByAddrAsyncComplete = completeBlock;
        [_arCoordByAddrPositioningObjs addObject:obj];
        if (_bGettingCoordByAddr == NO)
        {
            [self beginGetCoordByAddr];
        }
    }];
    return YES;
}

- (void)beginGetCoordByAddr
{
    if (_arCoordByAddrPositioningObjs.count == 0)
    {
        return;
    }
    _bGettingCoordByAddr = YES;
    XLGPositioningObj *obj = [_arCoordByAddrPositioningObjs objectAtIndex:0];
    
    if (self.geoCodeSearch == nil)
    {
        self.geoCodeSearch = [[[BMKGeoCodeSearch alloc] init] autorelease_mm];
    }
    self.geoCodeSearch.delegate = self;
    BMKGeoCodeSearchOption *geoCodeOption = [[BMKGeoCodeSearchOption new] autorelease_mm];
    geoCodeOption.address = obj.address;
    geoCodeOption.city = obj.city;
    [self.geoCodeSearch geoCode:geoCodeOption];
}

- (BOOL)cancelGetCoordByAddrWithReceiver:(id<XLGPositioningHelperDelegate>)receiver
{
    [_lockQueue dispatchThreadSync:^
    {
        NSMutableArray *arDel = [[NSMutableArray new] autorelease_mm];
        for (XLGPositioningObj *obj in _arCoordByAddrPositioningObjs)
        {
            if (obj.receiver == receiver)
            {
                if (obj.getCoordByAddrAsyncComplete != nil)
                {
                    DispatchMainThreadAsync(^
                    {
                        obj.getCoordByAddrAsyncComplete(NO, [[[CLLocation alloc] initWithLatitude:0.0 longitude:0.0] autorelease_mm], nil, [NSError errorWithDomain:@"取消地址搜索" code:XLGPositioningErrorCancel userInfo:nil]);
                    });
                }
                [arDel addObject:obj];
            }
        }
        
        [_arCoordByAddrPositioningObjs removeObjectsInArray:arDel];
        
        _bGettingCoordByAddr = NO;
        
        if (_arCoordByAddrPositioningObjs.count == 0)
        {
            [self cancelGetCoordByAddr];
        }
    }];
    return YES;
}

- (void)cancelGetCoordByAddr
{
    _bGettingCoordByAddr = NO;
    self.geoCodeSearch.delegate = nil;
    self.geoCodeSearch = nil;
}

- (void)onGetGeoCodeResult:(BMKGeoCodeSearch *)searcher result:(BMKGeoCodeResult *)result errorCode:(BMKSearchErrorCode)error
{
    NSError *theError = nil;
    if (error != BMK_SEARCH_NO_ERROR)
    {
        theError = [NSError errorWithDomain:@"获取地址失败" code:XLGPositioningErrorFail userInfo:nil];
    }
    
    [_lockQueue dispatchThreadSync:^
    {
        [self publicCoordByAddr:result error:theError];
        if (_arCoordByAddrPositioningObjs.count == 0)
        {
            [self cancelGetCoordByAddr];
        }
        else
        {
            [self beginGetCoordByAddr];
        }
    }];
}

- (void)publicCoordByAddr:(BMKGeoCodeResult *)result error:(NSError *)error
{
    NSMutableArray *arDel = [[NSMutableArray new] autorelease_mm];
    if (_arCoordByAddrPositioningObjs.count > 0)
    {
        [arDel addObject:[_arCoordByAddrPositioningObjs objectAtIndex:0]];
    }
    [arDel enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop)
    {
        XLGPositioningObj *posObj = obj;
        
        GetCoordByAddrAsyncCompletion getCoordByAddrAsyncComplete = posObj.getCoordByAddrAsyncComplete;
        id<XLGPositioningHelperDelegate> receiver = posObj.receiver;
        
        DispatchMainThreadAsync(^
        {
            if(getCoordByAddrAsyncComplete == nil)
            {
                [receiver positioningHelperUpdateCoord:[[[CLLocation alloc] initWithLatitude:result.location.latitude longitude:result.location.longitude] autorelease_mm] userInfo:posObj.userInfo error:error];
            }
            else
            {
                getCoordByAddrAsyncComplete(YES, [[[CLLocation alloc] initWithLatitude:result.location.latitude longitude:result.location.longitude] autorelease_mm], result.address, error);
            }
        });
    }];
    [_arCoordByAddrPositioningObjs removeObjectsInArray:arDel];
}


#pragma mark - get addr

- (BOOL)addAddrReceiver:(id<XLGPositioningHelperDelegate>)receiver coordinate:(CLLocationCoordinate2D)coord userInfo:(id)userInfo completeBlock:(GetResultAsyncCompletion)completeBlock
{
    [_lockQueue dispatchThreadSync:^
    {
        XLGPositioningObj *obj = [[XLGPositioningObj new] autorelease_mm];
        obj.receiver = receiver;
        obj.coord = coord;
        obj.userInfo = userInfo;
        obj.getResultAsyncComplete = completeBlock;
        [_arAddrPositioningObjs addObject:obj];
        if (_bGettingAddr == NO)
        {
            [self beginGetAddress];
        }
    }];
    
    return YES;
}

- (void)beginGetAddress
{
    if (_arAddrPositioningObjs.count == 0)
    {
        return;
    }
    _bGettingAddr = YES;
    XLGPositioningObj *obj = [_arAddrPositioningObjs objectAtIndex:0];
    
    if (self.reverseGeoCodeSearch == nil)
    {
        self.reverseGeoCodeSearch = [[BMKGeoCodeSearch new] autorelease_mm];
    }
    self.reverseGeoCodeSearch.delegate = self;
    BMKReverseGeoCodeOption *reverseGeoCodeOption = [[BMKReverseGeoCodeOption new] autorelease_mm];
    reverseGeoCodeOption.reverseGeoPoint = obj.coord;
    [self.reverseGeoCodeSearch reverseGeoCode:reverseGeoCodeOption];
}

- (BOOL)cancelGetAddrWithReceiver:(id<XLGPositioningHelperDelegate>)receiver
{
    [_lockQueue dispatchThreadSync:^
    {
        NSMutableArray *arDel = [[NSMutableArray new] autorelease_mm];
        for (XLGPositioningObj *obj in _arAddrPositioningObjs)
        {
            if (obj.receiver == receiver)
            {
                if (obj.getResultAsyncComplete != nil)
                {
                    DispatchMainThreadAsync(^
                    {
                        obj.getResultAsyncComplete(NO, nil, [NSError errorWithDomain:@"取消地址搜索" code:XLGPositioningErrorCancel userInfo:nil]);
                    });
                }
                [arDel addObject:obj];
            }
        }
        
        [_arAddrPositioningObjs removeObjectsInArray:arDel];
        
        _bGettingAddr = NO;
        
        if (_arAddrPositioningObjs.count == 0)
        {
            [self cancelGetAddress];
        }
    }];
    return YES;
}

- (void)cancelGetAddress
{
    _bGettingAddr = NO;
    self.reverseGeoCodeSearch.delegate = nil;
    self.reverseGeoCodeSearch = nil;

}

- (void)onGetReverseGeoCodeResult:(BMKGeoCodeSearch *)searcher result:(BMKReverseGeoCodeResult *)result errorCode:(BMKSearchErrorCode)error
{
    NSError *theError = nil;
    if (error != BMK_SEARCH_NO_ERROR)
    {
        theError = [NSError errorWithDomain:@"获取地址失败" code:XLGPositioningErrorFail userInfo:nil];
    }
    
    [_lockQueue dispatchThreadSync:^
    {
        [self publicAddr:result error:theError];
        if (_arAddrPositioningObjs.count == 0)
        {
            [self cancelGetAddress];
        }
        else
        {
            [self beginGetAddress];
        }
    }];
}

- (void)publicAddr:(BMKReverseGeoCodeResult *)result error:(NSError *)error
{
    NSMutableArray *arDel = [[NSMutableArray new] autorelease_mm];
    if (_arAddrPositioningObjs.count > 0)
    {
        [arDel addObject:[_arAddrPositioningObjs objectAtIndex:0]];
    }
    for (int i = 1; i < _arAddrPositioningObjs.count; i++)
    {
        XLGPositioningObj *obj = [_arAddrPositioningObjs objectAtIndex:i];
        if (obj.coord.latitude == result.location.latitude && obj.coord.longitude == result.location.longitude)
        {
            [arDel addObject:obj];
        }
    }
    [arDel enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop)
    {
        XLGPositioningObj *posObj = obj;
        
        id<XLGPositioningHelperDelegate> receiver = posObj.receiver;
        GetResultAsyncCompletion getResultAsyncComplete = posObj.getResultAsyncComplete;
        
        DispatchMainThreadAsync(^
        {
            if (getResultAsyncComplete == nil)
            {
                [receiver positioningHelperUpdateAddr:result userInfo:posObj.userInfo error:error];
            }
            else
            {
                getResultAsyncComplete(YES, result, error);
            }
        });
    }];
    [_arAddrPositioningObjs removeObjectsInArray:arDel];
}

#pragma mark - get route

- (BOOL)addRouteReceiver:(id)receiver startNode:(BMKPlanNode *)startNode endNode:(BMKPlanNode *)endNode type:(XLGRouteSearchType)type completeBlock:(GetResultAsyncCompletion)completeBlock
{
    [_lockQueue dispatchThreadSync:^
    {
        XLGPositioningObj *obj = [[XLGPositioningObj new] autorelease_mm];
        obj.receiver = receiver;
        obj.startNode = startNode;
        obj.endNode = endNode;
        obj.routeType = type;
        obj.getResultAsyncComplete = completeBlock;
        [_arRoutePositioningObjs addObject:obj];
        if (_bGettingRoute == NO)
        {
            [self beginGetRoute];
        }
    }];
    return YES;
}

- (void)beginGetRoute
{
    if (_arRoutePositioningObjs.count == 0)
    {
        return;
    }
    
    _bGettingRoute = YES;
    XLGPositioningObj *obj = [_arRoutePositioningObjs objectAtIndex:0];

    if (self.routeSearch == nil)
    {
        self.routeSearch = [[BMKRouteSearch alloc] init];
    }
    self.routeSearch.delegate = self;

    BOOL flag = YES;
    switch (obj.routeType)
    {
        case XLGRouteSearchTypeCar:
        {
            BMKDrivingRoutePlanOption *drivingRouteSearchOption = [[BMKDrivingRoutePlanOption new] autorelease_mm];
            drivingRouteSearchOption.from = obj.startNode;
            drivingRouteSearchOption.to = obj.endNode;
            flag = [self.routeSearch drivingSearch:drivingRouteSearchOption];
            break;
        }
        case XLGRouteSearchTypeBus:
        {
            BMKTransitRoutePlanOption *transitRouteSearchOption = [[BMKTransitRoutePlanOption new] autorelease_mm];
            transitRouteSearchOption.city = obj.startNode.cityName;
            transitRouteSearchOption.from = obj.startNode;
            transitRouteSearchOption.to = obj.endNode;
            flag = [self.routeSearch transitSearch:transitRouteSearchOption];
            break;
        }
        case XLGRouteSearchTypeWalk:
        {
            BMKWalkingRoutePlanOption *walkingRouteSearchOption = [[BMKWalkingRoutePlanOption new] autorelease_mm];
            walkingRouteSearchOption.from = obj.startNode;
            walkingRouteSearchOption.to = obj.endNode;
            flag = [self.routeSearch walkingSearch:walkingRouteSearchOption];
            break;
        }
        default:
            break;
    }
    if (flag == NO)
    {
        [self searchRouteCompleteWithResult:nil errorCode:1];
    }
}

- (BOOL)cancelGetRouteWithReceiver:(id)receiver
{
    [_lockQueue dispatchThreadSync:^
    {
        NSMutableArray *arDel = [[NSMutableArray new] autorelease_mm];
        for (XLGPositioningObj *obj in _arRoutePositioningObjs)
        {
            if (obj.receiver == receiver)
            {
                if (obj.getResultAsyncComplete != nil)
                {
                    DispatchMainThreadAsync(^
                    {
                        obj.getResultAsyncComplete(NO, nil, [NSError errorWithDomain:@"取消路线搜索" code:XLGPositioningErrorCancel userInfo:nil]);
                    });
                }
                [arDel addObject:obj];
            }
        }
        
        [_arRoutePositioningObjs removeObjectsInArray:arDel];
        
        _bGettingRoute = NO;
        
        if (_arRoutePositioningObjs.count == 0)
        {
            [self cancelGetRoute];
        }
    }];
    return YES;
}

- (void)cancelGetRoute
{
    _bGettingRoute = NO;
    self.routeSearch.delegate = nil;
    self.routeSearch = nil;
}

- (void)onGetTransitRouteResult:(BMKRouteSearch*)searcher result:(BMKTransitRouteResult*)result errorCode:(BMKSearchErrorCode)error
{
    [self searchRouteCompleteWithResult:result errorCode:error];
}

- (void)onGetDrivingRouteResult:(BMKRouteSearch*)searcher result:(BMKDrivingRouteResult*)result errorCode:(BMKSearchErrorCode)error
{
    [self searchRouteCompleteWithResult:result errorCode:error];
}

- (void)onGetWalkingRouteResult:(BMKRouteSearch*)searcher result:(BMKWalkingRouteResult*)result errorCode:(BMKSearchErrorCode)error
{
    [self searchRouteCompleteWithResult:result errorCode:error];
}

- (void)searchRouteCompleteWithResult:(id)result errorCode:(int)error
{
    NSError *theError = nil;
    if (error != 0)
    {
        theError = [NSError errorWithDomain:@"" code:XLGPositioningErrorFail userInfo:nil];
    }
    
    [_lockQueue dispatchThreadSync:^
    {
        [self publicRoutes:result error:theError];
        if (_arRoutePositioningObjs.count == 0)
        {
            [self cancelGetRoute];
        }
        else
        {
            [self beginGetRoute];
        }
    }];
}

- (void)publicRoutes:(id)result error:(NSError *)error
{
    NSMutableArray *arDel = [[NSMutableArray new] autorelease_mm];
    if (_arRoutePositioningObjs.count > 0)
    {
        [arDel addObject:[_arRoutePositioningObjs objectAtIndex:0]];
    }
    [arDel enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop)
    {
        XLGPositioningObj *posObj = obj;
        if(posObj.getResultAsyncComplete != nil)
        {
            GetResultAsyncCompletion getResultAsyncComplete = posObj.getResultAsyncComplete;
            
            DispatchMainThreadAsync(^
            {
                getResultAsyncComplete(YES, result, error);
            });
        }
    }];
    [_arRoutePositioningObjs removeObjectsInArray:arDel];
}

@end
